/*
** $Id: lauxlib.h $
** 用于构建Lua库的辅助函数
** See Copyright Notice in lua.h
*/


#ifndef lauxlib_h
#define lauxlib_h


#include <stddef.h>
#include <stdio.h>

#include "luaconf.h"
#include "lua.h"


/* 全局表名字 */
#define LUA_GNAME	"_G"


typedef struct luaL_Buffer luaL_Buffer;


/* 'luaL_loadfilex'的额外错误码 */
#define LUA_ERRFILE     (LUA_ERRERR+1)


/* 注册表中用于所已加载模块的表的key */
#define LUA_LOADED_TABLE	"_LOADED"


/* 注册表中用于预加载器的表的key */
#define LUA_PRELOAD_TABLE	"_PRELOAD"

/* 注册函数(用于 luaL_setfuncs)，任何 luaL_Reg 数组必须以一对 name 与 func 皆为 NULL 结束。 */
typedef struct luaL_Reg {
  const char *name; // 函数名
  lua_CFunction func; // 函数指针
} luaL_Reg;


#define LUAL_NUMSIZES	(sizeof(lua_Integer)*16 + sizeof(lua_Number))

LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz);
/* 检查调用它的内核是否是创建这个 Lua 状态机的内核。 以及调用它的代码是否使用了相同的 Lua 版本
同时也检查调用它的内核与创建该 Lua 状态机的内核 是否使用了同一片地址空间 */
#define luaL_checkversion(L)  \
	  luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES)

/* 将obj索引处的元表中e字段压入栈 */
LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
/* 调用obj索引处的对象的元方法e */
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
/* 将idx索引处的值转为一个C字符串，结果不会压入栈，会由函数返回 */
LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len);
/*
** 抛出一个错误报告调用的 C 函数的第 arg 个参数的问题。 它使用下列标准信息并包含了一段 extramsg 作为注解：
**      bad argument #arg to 'funcname' (extramsg)
*/
LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg);
/** 抛出类型错误，第 arg 个参数期望 tname 类型 */
LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname);
/* 检查函数的第 arg 个参数是否是一个字符串，并返回改字符串 */
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg,
                                                          size_t *l);
/* 如果函数的第 arg 个参数是一个 字符串，返回该字符串。 若该参数不存在或是 nil， 返回 def。 */
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg,
                                          const char *def, size_t *l);
/* 检查函数的第 arg 个参数是否是一个数字（或是可以被转换为一个数字） 并以 lua_Number 类型返回这个整数值 */
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg);
/* 如果函数的第 arg 个参数是一个 数字（或可以转换为一个数字）， 返回该整数。 若该参数不存在或是 nil，返回 def */
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def);

/* 检查函数的第 arg 个参数是否是一个 整数（或是可以被转换为一个整数） 并以 lua_Integer 类型返回这个整数值 */
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg);
/* 如果函数的第 arg 个参数是一个 整数（或可以转换为一个整数），返回该整数。若该参数不存在或是nil，返回 def */
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg,
                                          lua_Integer def);

/* 将栈空间扩展到 top + sz 个元素 */
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
/* 检查函数的第 arg 个参数的类型是否是 t */
LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t);
/* 检查函数在 arg 位置是否有任何类型（包括 nil）的参数 */
LUALIB_API void (luaL_checkany) (lua_State *L, int arg);

/* 如果注册表中已经存在tname，返回0，否则，为用户数据的元表创建一张新表,向这张表加入 __name = tname 键值对， 并将 [tname] = new table 添加到注册表中， 返回 1 */
LUALIB_API int   (luaL_newmetatable) (lua_State *L, const char *tname);
/* 将注册表中 tname 关联的元表, 设为栈顶对象的元表。 */
LUALIB_API void  (luaL_setmetatable) (lua_State *L, const char *tname);
/* 测试函数第ud个参数是否是一个类型为tname的用户数据，失败时返回NULL */
LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
/* 检查函数的第 arg 个参数是否是一个类型为 tname 的用户数据, 返回该用户数据的地址 */
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);

/* 将一个用于表示 lvl 层栈的控制点位置的字符串压栈(用于构建错误消息前缀) */
LUALIB_API void (luaL_where) (lua_State *L, int lvl);
/* 引发错误 */
LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);

/*
** 将字符串映射为C枚举量
** 返回函数第arg个参数对应lst中的索引号
** def: 默认字符串
*/
LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def,
                                   const char *const lst[]);
/* 生成标准库中和文件相关函数的返回值 */
LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname);
/* 生成标准库中和进程相关函数的返回值 */
LUALIB_API int (luaL_execresult) (lua_State *L, int stat);


/* predefined references */
#define LUA_NOREF       (-2)
#define LUA_REFNIL      (-1)

LUALIB_API int (luaL_ref) (lua_State *L, int t);
/* 释放索引 t 处表的 ref 引用对象  */
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);

/* 把一段缓存加载为Lua代码 */
LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
                                               const char *mode);

/* 加载文件 */
#define luaL_loadfile(L,f)	luaL_loadfilex(L,f,NULL)

/* 把一段缓存加载为一个 Lua 代码块 */
LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz,
                                   const char *name, const char *mode);
/* 把一个字符串加载为Lua代码块 */
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);

/* 创建一个新的Lua状态机 */
LUALIB_API lua_State *(luaL_newstate) (void);

/* 以数字形式返回给定索引处值的“长度”； 它等价于在 Lua 中调用 '#' 的操作 */
LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);

LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s,
                                     const char *p, const char *r);
/* 将字符串 s 生成一个副本， 并将其中的所有字符串 p 都替换为字符串 r, 将结果串压栈并返回它 */
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s,
                                    const char *p, const char *r);

/* 将数组l中的所有函数，注册到栈顶的表中，nup:上值数，所有函数共享这些上值，这些值必须在调用前压入表之上，注册完毕后会从栈弹出 */
LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);

/* 确保t[fname]是一张表，idx指向t，如果t[fname]原本就是一张表返回真，否则为他创建一张新表，返回假 */
LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname);

/* 将栈 L1 的栈回溯信息压栈。 如果 msg 不为 NULL ，它会附加到栈回溯信息之前。 level 参数指明从第几层开始做栈回溯 */
LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1,
                                  const char *msg, int level);
/*
* *如果 modname 不在 package.loaded 中， 则调用函数 openf ，并传入字符串 modname
** 将其返回值置入 package.loaded[modname]。 这个行为好似该函数通过 require 调用过一样。
** 如果 glb 为真， 同时也将模块设到全局变量 modname 里。
** 在栈上留下该模块的副本。
*/
LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
                                 lua_CFunction openf, int glb);

/*
** ===============================================================
** some useful macros
** ===============================================================
*/

/* 创建一张新表，并预分配足够保存下l内容的空间 */
#define luaL_newlibtable(L,l)	\
  lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)

/* 创建一张新表，并把l中的函数注册进去 */
#define luaL_newlib(L,l)  \
  (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))

#define luaL_argcheck(L, cond,arg,extramsg)	\
	((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg))))

#define luaL_argexpected(L,cond,arg,tname)	\
	((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname))))

/*
** const char *luaL_checkstring (lua_State *L, int arg);
** 检查函数的第 arg 个参数是否是一个 字符串并返回这个字符串
*/
#define luaL_checkstring(L,n)	(luaL_checklstring(L, (n), NULL))
/* 如果函数的第 arg 个参数是一个 字符串，返回该字符串。 若该参数不存在或是 nil， 返回 d */
#define luaL_optstring(L,n,d)	(luaL_optlstring(L, (n), (d), NULL))

/* 获取i索引的值类型名 */
#define luaL_typename(L,i)	lua_typename(L, lua_type(L,(i)))

/* 加载并运行指定的文件, 没有错误，返回假, 有错返回真。 */
#define luaL_dofile(L, fn) \
	(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))

/* 加载并运行指定的字符串, 没有错误，返回假, 有错返回真。 */
#define luaL_dostring(L, s) \
	(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))

/* 将注册表中n对应的元表压栈。如果没有 tname 对应的元表，则将 nil 压栈并返回假。*/
#define luaL_getmetatable(L,n)	(lua_getfield(L, LUA_REGISTRYINDEX, (n)))

#define luaL_opt(L,f,n,d)	(lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))

#define luaL_loadbuffer(L,s,sz,n)	luaL_loadbufferx(L,s,sz,n,NULL)


/*
** Perform arithmetic operations on lua_Integer values with wrap-around
** semantics, as the Lua core does.
*/
#define luaL_intop(op,v1,v2)  \
	((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2)))


/* push the value used to represent failure/error */
#define luaL_pushfail(L)	lua_pushnil(L)


/*
** Internal assertions for in-house debugging
*/
#if !defined(lua_assert)

#if defined LUAI_ASSERT
  #include <assert.h>
  #define lua_assert(c)		assert(c)
#else
  #define lua_assert(c)		((void)0)
#endif

#endif



/*
** {======================================================
** Generic Buffer manipulation
** =======================================================
*/

/*
** 字符串缓存 的类型。
** 字符串缓存可以让 C 代码分段构造一个 Lua 字符串。 使用模式如下：
** 首先定义一个类型为 luaL_Buffer 的变量 b。
** 调用 luaL_buffinit(L, &b) 初始化它。
** 然后调用 luaL_add* 这组函数向其添加字符串片断。
** 最后调用 luaL_pushresult(&b)。调用会在栈顶留下最终的字符串。
**
** 如果你预先知道结果串的长度， 你可以这样使用缓存： 
** 首先定义一个类型为 luaL_Buffer 的变量 b。
** 然后调用 luaL_buffinitsize(L, &b, sz) 预分配 sz 大小的空间。
** 接着将字符串复制入这个空间。
** 最后调用 luaL_pushresultsize(&b, sz)， 这里的 sz 指已经复制到缓存内的字符串长度。
** 一般的操作过程中，字符串缓存会使用不定量的栈槽。因此，在使用缓存中，你不能假定目前栈顶在哪。
** 在对缓存操作的函数调用间，你都可以使用栈，只需要保证栈平衡即可；
** 即，在你做一次缓存操作调用时，当时的栈位置和上次调用缓存操作后的位置相同。（luaL_addvalue 是个唯一的例外。）
** 在调用完 luaL_pushresult 后， 栈会恢复到缓存初始化时的位置上，并在顶部压入最终的字符串。
*/
struct luaL_Buffer {
  char *b;  /* 缓冲地址 */
  size_t size;  /* 缓冲大小 */
  size_t n;  /* 缓冲中的字符数 */
  lua_State *L;
  union {
    LUAI_MAXALIGN;  /* 保证缓冲的最大对齐 */
    char b[LUAL_BUFFERSIZE];  /* 初始化缓冲 */
  } init;
};


#define luaL_bufflen(bf)	((bf)->n)
#define luaL_buffaddr(bf)	((bf)->b)


/* luaL_addchar(luaL_Buffer B, char c) 向B中添加一个字节c */
#define luaL_addchar(B,c) \
  ((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \
   ((B)->b[(B)->n++] = (c)))

#define luaL_addsize(B,s)	((B)->n += (s))

#define luaL_buffsub(B,s)	((B)->n -= (s))

/* 初始化luaL_Buffer */
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
/* 返回一段大小为 sz 的空间地址。 你可以将字符串复制其中以加到缓存 B 内 （参见 luaL_Buffer）。 将字符串复制其中后，你必须调用 luaL_addsize 传入字符串的大小，才会真正把它加入缓存。 */
LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);
/* 向B中添加一个字符串(lstring:不以\0为字符串结尾) */
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
/* 向B中添加一个字符串 */
LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
/* 向B添加栈顶的一个值，随后将其弹出。 */
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
/* 结束对缓存 B 的使用，将最终的字符串留在栈顶 */
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
/* 等价于 luaL_addsize，luaL_pushresult。 */
LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz);
/* 初始化luaL_Buffer并且预分配指定内存空间 */
LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz);

#define luaL_prepbuffer(B)	luaL_prepbuffsize(B, LUAL_BUFFERSIZE)

/* }====================================================== */



/*
** {======================================================
** IO库的文件句柄
** =======================================================
*/

/*
** 文件句柄是一个拥有元表'LUA_FILEHANDLE'和初始化结构'luaL_Stream'(初始化结构后面可能包含其他字段)的userdata.
*/

#define LUA_FILEHANDLE          "FILE*"

/* 标准io库中用到的标准文件句柄结构 */
typedef struct luaL_Stream {
  FILE *f;  /* stream, (未完全创建时为NULL) */
  lua_CFunction closef;  /* 关闭stream的方法 (对于已经关闭的stream位NULL) */
} luaL_Stream;

/* }====================================================== */

/*
** {==================================================================
** "Abstraction Layer(抽象层)" for basic report of messages and errors
** ===================================================================
*/

/* 打印字符串 */
#if !defined(lua_writestring)
#define lua_writestring(s,l)   fwrite((s), sizeof(char), (l), stdout)
#endif

/* 打印一个新行并刷新输出 */
#if !defined(lua_writeline)
#define lua_writeline()        (lua_writestring("\n", 1), fflush(stdout))
#endif

/* 打印错误信息 */
#if !defined(lua_writestringerror)
#define lua_writestringerror(s,p) \
        (fprintf(stderr, (s), (p)), fflush(stderr))
#endif

/* }================================================================== */


/*
** {============================================================
** Compatibility with deprecated conversions
** =============================================================
*/
#if defined(LUA_COMPAT_APIINTCASTS)

#define luaL_checkunsigned(L,a)	((lua_Unsigned)luaL_checkinteger(L,a))
#define luaL_optunsigned(L,a,d)	\
	((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d)))

#define luaL_checkint(L,n)	((int)luaL_checkinteger(L, (n)))
#define luaL_optint(L,n,d)	((int)luaL_optinteger(L, (n), (d)))

#define luaL_checklong(L,n)	((long)luaL_checkinteger(L, (n)))
#define luaL_optlong(L,n,d)	((long)luaL_optinteger(L, (n), (d)))

#endif
/* }============================================================ */



#endif


